Teorema da Convolução

Convolução periódica

Antes de falarmos sobre o Teorema da Convolução, precisamos entender a convolução periódica (pconv). Até agora, vimos a convolução linear (conv ou scipy.signal.convolve2d), onde o kernel $h$ tem sua origem no centro e a imagem $f$ tem sua origem no canto superior esquerdo. Na convolução periódica, a origem do kernel $h$ está na origem da imagem $f$. Ambos kernel e imagem são periódicos, com o mesmo período. Como normalmente o kernel $h$ é muito menor que a imagem $f$, ele é preenchido com zeros até o tamanho de $f$.


In [27]:
%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
from numpy.fft import *
import sys,os
ia898path = os.path.abspath('../../')
if ia898path not in sys.path:
    sys.path.append(ia898path)
import ia898.src as ia

In [28]:
f = np.array([[1,0,0,0,0,0,0,0,0],
             [0,0,0,0,0,0,0,0,0],
             [0,0,0,1,0,0,0,0,0],
             [0,0,0,0,0,0,0,0,1],
             [0,0,0,0,0,0,0,0,0]])
print("Image (f):")
print(f)
    
h = np.array([[1,2,3],
             [4,5,6]])
print("\n Image Kernel (h):")
print(h)
    
g1 = ia.pconv(f,h)
print("Image Output (pconv):")
print(g1)


Image (f):
[[1 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0]
 [0 0 0 1 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 1]
 [0 0 0 0 0 0 0 0 0]]

 Image Kernel (h):
[[1 2 3]
 [4 5 6]]
Image Output (pconv):
[[ 1.  2.  3.  0.  0.  0.  0.  0.  0.]
 [ 4.  5.  6.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  1.  2.  3.  0.  0.  0.]
 [ 2.  3.  0.  4.  5.  6.  0.  0.  1.]
 [ 5.  6.  0.  0.  0.  0.  0.  0.  4.]]

Teorema da convolução

O teorema da convolução diz que $$ F(f * g) = F(f) \cdot F(g) $$ $$ F(f\cdot g) = F(f) * F(g) $$

onde $F$ indica o operador da transformada de Fourier, ou seja, $F(f)$ and $F(g)$ são as transformdas de $f$ e $g$. É importante perceber que a convolução usada aqui é a convolução periódica.

Vamos ilustrar o Teorema da Convolução com um exemplo numérico. Primeiro, vamos calcular a convolução periódica de uma imagem $f$ com um kernel $h$


In [29]:
fr = np.linspace(-1,1,6)
f  = np.array([fr,2*fr,fr,fr]) 
print(f)


[[-1.  -0.6 -0.2  0.2  0.6  1. ]
 [-2.  -1.2 -0.4  0.4  1.2  2. ]
 [-1.  -0.6 -0.2  0.2  0.6  1. ]
 [-1.  -0.6 -0.2  0.2  0.6  1. ]]

In [30]:
hh = np.array([-1,0,+1])
h = np.array([hh,2*hh,hh])
print(h)


[[-1  0  1]
 [-2  0  2]
 [-1  0  1]]

In [31]:
g = ia.pconv(f,h)
print(g)


[[ 6.4  6.4 -3.2 -3.2 -3.2 -3.2]
 [ 8.   8.  -4.  -4.  -4.  -4. ]
 [ 9.6  9.6 -4.8 -4.8 -4.8 -4.8]
 [ 8.   8.  -4.  -4.  -4.  -4. ]]

Agora, vamos calcular a transformada de Fourier $F(f)$ da imagem e $F(h)$ do kernel. Antes de mais nada, precisamos garantir que a imagem $f$ e o kernel $h$ sejam periódicos e tenham o mesmo tamanho.


In [32]:
# Aumentando h para o tamanho de f
aux = np.zeros(f.shape)
r,c = h.shape
aux[:r,:c] = h

# Calculando a Transformada de Fourier da f e h
F = fft2(f)
H = fft2(aux)

# Multiplicando-se as Tranformadas
G = F * H

# Calculando a Transformada inversa
gg = ifft2(G)

print("Result gg: \n",np.around(gg))


Result gg: 
 [[  6.-0.j   6.-0.j  -3.-0.j  -3.+0.j  -3.+0.j  -3.-0.j]
 [  8.-0.j   8.-0.j  -4.-0.j  -4.+0.j  -4.+0.j  -4.-0.j]
 [ 10.-0.j  10.-0.j  -5.-0.j  -5.-0.j  -5.+0.j  -5.-0.j]
 [  8.-0.j   8.-0.j  -4.-0.j  -4.+0.j  -4.+0.j  -4.-0.j]]

Pelo teorema da convolução, gg e g deveriam ser iguais:


In [33]:
print('O teorema da convolução funcionou?', np.allclose(gg.real,g))


O teorema da convolução funcionou? True